package com.sc.nft.service.impl;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Lists;
import com.sc.nft.dao.*;
import com.sc.nft.dto.*;
import com.sc.nft.entity.*;
import com.sc.nft.entity.vo.UserPointsDetailsStatisticsVO;
import com.sc.nft.entity.vo.ecology.*;
import com.sc.nft.entity.vo.merchantPledgeTask.AdminEcologyPointsDataVO;
import com.sc.nft.enums.*;
import com.sc.nft.exception.GlobalRunTimeException;
import com.sc.nft.service.UserEcologyPointsService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

@Slf4j
@RequiredArgsConstructor
@Service
public class UserEcologyPointsImpl implements UserEcologyPointsService {

    private final UserEcologyPointsDao userEcologyPointsDao;
    private final UserEcologyPointsDetailDao userEcologyPointsDetailDao;
    private final UserEcologyPointsQuotaLogDao userEcologyPointsQuotaLogDao;
    private final UserEcologyPointsQuotaRecordsDao userEcologyPointsQuotaRecordsDao;
    private final UserEcologyHatchRecordsDao userEcologyHatchRecordsDao;
    private final UserEcologyHatchIncomeRecordDao userEcologyHatchIncomeRecordDao;
    private final DaoEcologyPointsDao daoEcologyPointsDao;
    private final UserInfoDao userInfoDao;
    private final UserEcologyUsableAmountUpperLimitDetailDao userEcologyUsableAmountUpperLimitDetailDao;

    @Override
    public UserEcologyPoints getOneByUserId(Long userId) {
        return userEcologyPointsDao.getOneByUserId(userId);
    }

    @Override
    public Boolean subAmount(Long userId, BigDecimal amount,EcologyPointsEnum type,String remark) {

        UserEcologyPoints userEcologyPoints = getOneByUserId(userId);

        Boolean flag = userEcologyPointsDao.subAmount(userId, amount);
        //日志
        UserEcologyPointsDetail userEcologyPointsDetail = new UserEcologyPointsDetail();
        userEcologyPointsDetail.setUserId(userId);
        userEcologyPointsDetail.setType(type);
        userEcologyPointsDetail.setAmount(amount);
        userEcologyPointsDetail.setRealAmount(amount);
        userEcologyPointsDetail.setBeforeBalance(userEcologyPoints.getAmount());
        userEcologyPointsDetail.setAfterBalance(userEcologyPoints.getAmount().subtract(amount));
        userEcologyPointsDetail.setIsAdd(type.getIsAdd());
        userEcologyPointsDetail.setRemark(remark);
//        userEcologyPointsDetail.insert();
        userEcologyPointsDetailDao.save(userEcologyPointsDetail);

        return flag;
    }

    @Override
    public Boolean addEcologyPoints(Long userId, BigDecimal amount,EcologyPointsEnum type,String remark) {
        UserEcologyPoints userEcologyPoints = getOneByUserId(userId);

        Boolean flag = userEcologyPointsDao.addAmount(userId, amount);
        //日志
        UserEcologyPointsDetail userEcologyPointsDetail = new UserEcologyPointsDetail();
        userEcologyPointsDetail.setUserId(userId);
        userEcologyPointsDetail.setType(type);
        userEcologyPointsDetail.setAmount(amount);
        userEcologyPointsDetail.setRealAmount(amount);
        userEcologyPointsDetail.setBeforeBalance(userEcologyPoints.getAmount());
        userEcologyPointsDetail.setAfterBalance(userEcologyPoints.getAmount().add(amount));
        userEcologyPointsDetail.setIsAdd(type.getIsAdd());
        userEcologyPointsDetail.setRemark(remark);
//        userEcologyPointsDetail.insert();
        userEcologyPointsDetailDao.save(userEcologyPointsDetail);
        return flag;
    }

    @Override
    public Boolean addDynamicStateEcologyPoints(Long userId, BigDecimal amount, EcologyPointsEnum type,EcologyIncomeTypeEnum incomeType, String remark,String daoIncomeName) {
        UserEcologyPoints userEcologyPoints = getOneByUserId(userId);
        BigDecimal usableAmountUpperLimit = userEcologyPoints.getUsableAmountUpperLimit();
        //1.上限大于等于金额  则全部收益都加入到可用余额
        if(NumberUtil.isGreaterOrEqual(usableAmountUpperLimit,amount)){

            UserEcologyPointsQuotaRecords userEcologyPointsQuotaRecords = new UserEcologyPointsQuotaRecords();
            userEcologyPointsQuotaRecords.setUserId(userId);
            userEcologyPointsQuotaRecords.setIncomeType(incomeType);
            userEcologyPointsQuotaRecords.setIncomeTypeName(StrUtil.isNotBlank(daoIncomeName) ? daoIncomeName : incomeType.getDescription());
            userEcologyPointsQuotaRecords.setIncomeAmount(amount);
            userEcologyPointsQuotaRecords.setReleaseAmount(amount);
            userEcologyPointsQuotaRecords.setFreezeAmount(BigDecimal.ZERO);
            userEcologyPointsQuotaRecords.setGetTime(DateTime.now());
            userEcologyPointsQuotaRecords.setEffectiveTime(DateUtil.offsetDay(DateTime.now(),10000));
            userEcologyPointsQuotaRecords.insert();

            UserEcologyUsableAmountUpperLimitDetail userEcologyUsableAmountUpperLimitDetail = new UserEcologyUsableAmountUpperLimitDetail();
            userEcologyUsableAmountUpperLimitDetail.setUserId(userId);
            userEcologyUsableAmountUpperLimitDetail.setType(UserEcologyUsableAmountUpperLimitDetailTypeEnum.FROZEN_FUEL_RELEASE);
            userEcologyUsableAmountUpperLimitDetail.setTypeName(UserEcologyUsableAmountUpperLimitDetailTypeEnum.FROZEN_FUEL_RELEASE.getDescription());
            userEcologyUsableAmountUpperLimitDetail.setAmount(amount);
            userEcologyUsableAmountUpperLimitDetail.setBeforeBalance(usableAmountUpperLimit);
            userEcologyUsableAmountUpperLimitDetail.setAfterBalance(usableAmountUpperLimit.subtract(amount));
            userEcologyUsableAmountUpperLimitDetail.setIsAdd(UserEcologyUsableAmountUpperLimitDetailTypeEnum.FROZEN_FUEL_RELEASE.getIsAdd());
            userEcologyUsableAmountUpperLimitDetail.insert();

            return addEcologyPointsAndSubLimit(userId,amount,type,remark);
        }else{
            //2.上限小于金额  则部分收益加入到可用余额
            //冻结金额 = 收益金额 - 可用余额上限
            BigDecimal freezeAmount = amount.subtract(usableAmountUpperLimit).setScale(2, BigDecimal.ROUND_DOWN);

            UserEcologyPointsQuotaRecords userEcologyPointsQuotaRecords = new UserEcologyPointsQuotaRecords();
            userEcologyPointsQuotaRecords.setUserId(userId);
            userEcologyPointsQuotaRecords.setIncomeType(incomeType);
            userEcologyPointsQuotaRecords.setIncomeTypeName(StrUtil.isNotBlank(daoIncomeName) ? daoIncomeName : incomeType.getDescription());
            userEcologyPointsQuotaRecords.setIncomeAmount(amount);
            userEcologyPointsQuotaRecords.setReleaseAmount(usableAmountUpperLimit);
            userEcologyPointsQuotaRecords.setFreezeAmount(freezeAmount);
            userEcologyPointsQuotaRecords.setGetTime(DateTime.now());
            userEcologyPointsQuotaRecords.setEffectiveTime(DateUtil.offsetDay(DateTime.now(),30));
            userEcologyPointsQuotaRecords.insert();

            if(NumberUtil.isGreater(usableAmountUpperLimit,BigDecimal.ZERO)){
                UserEcologyUsableAmountUpperLimitDetail userEcologyUsableAmountUpperLimitDetail = new UserEcologyUsableAmountUpperLimitDetail();
                userEcologyUsableAmountUpperLimitDetail.setUserId(userId);
                userEcologyUsableAmountUpperLimitDetail.setType(UserEcologyUsableAmountUpperLimitDetailTypeEnum.FROZEN_FUEL_RELEASE);
                userEcologyUsableAmountUpperLimitDetail.setTypeName(UserEcologyUsableAmountUpperLimitDetailTypeEnum.FROZEN_FUEL_RELEASE.getDescription());
                userEcologyUsableAmountUpperLimitDetail.setAmount(usableAmountUpperLimit);
                userEcologyUsableAmountUpperLimitDetail.setBeforeBalance(usableAmountUpperLimit);
                userEcologyUsableAmountUpperLimitDetail.setAfterBalance(BigDecimal.ZERO);
                userEcologyUsableAmountUpperLimitDetail.setIsAdd(UserEcologyUsableAmountUpperLimitDetailTypeEnum.FROZEN_FUEL_RELEASE.getIsAdd());
                userEcologyUsableAmountUpperLimitDetail.insert();

                return addEcologyPointsAndSubLimit(userId,usableAmountUpperLimit,type,remark);
            }else{
                return Boolean.TRUE;
            }
        }
    }

    @Override
    public Boolean addEcologyPointsAndSubLimit(Long userId, BigDecimal amount, EcologyPointsEnum type, String remark) {
        UserEcologyPoints userEcologyPoints = getOneByUserId(userId);
        Boolean flag = userEcologyPointsDao.addEcologyPointsAndSubLimit(userId, amount);
        //日志
        UserEcologyPointsDetail userEcologyPointsDetail = new UserEcologyPointsDetail();
        userEcologyPointsDetail.setUserId(userId);
        userEcologyPointsDetail.setType(type);
        userEcologyPointsDetail.setAmount(amount);
        userEcologyPointsDetail.setRealAmount(amount);
        userEcologyPointsDetail.setBeforeBalance(userEcologyPoints.getAmount());
        userEcologyPointsDetail.setAfterBalance(userEcologyPoints.getAmount().add(amount));
        userEcologyPointsDetail.setIsAdd(type.getIsAdd());
        userEcologyPointsDetail.setRemark(remark);
//        userEcologyPointsDetail.insert();
        userEcologyPointsDetailDao.save(userEcologyPointsDetail);
        return flag;
    }

    @Override
    public Page<AdminEcologyPointsUserPageVO> getUserPage(String userTel, Integer pageNo, Integer pageSize) {
        return userEcologyPointsDao.getUserPage(userTel,pageNo,pageSize);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void editUserPoints(Long id, Boolean isAdd, String amountString, String remark) {
        UserEcologyPoints userEcologyPoints = userEcologyPointsDao.getById(id);
        Assert.notNull(userEcologyPoints,()-> new GlobalRunTimeException("用户燃料不存在"));

        //要加的金额
        BigDecimal amount = Convert.toBigDecimal(amountString).setScale(2,BigDecimal.ROUND_DOWN);
        Assert.isTrue(NumberUtil.isGreater(amount,BigDecimal.ZERO),()-> new GlobalRunTimeException("金额不能小于0"));
        if (isAdd) {//加
            Assert.isTrue(addEcologyPoints(userEcologyPoints.getUserId(),amount,EcologyPointsEnum.SYSTEM_ADD,remark),()-> new GlobalRunTimeException("添加燃料失败"));
        } else {//减
            Assert.isTrue(subAmount(userEcologyPoints.getUserId(),amount,EcologyPointsEnum.SYSTEM_SUB,remark),()-> new GlobalRunTimeException("用户燃料不足"));
        }

    }

    @Override
    public Page<AdminEcologyPointsUserPageDetailsVO> getUserDetailsPage(Long userId,EcologyPointsEnum type, Date startTime, Date endTime, Integer pageNo, Integer pageSize) {
        Page<AdminEcologyPointsUserPageDetailsVO> result = userEcologyPointsDetailDao.getUserDetailsPage(new Page<>(pageNo, pageSize), userId, ObjectUtil.isNull(type) ? null : type.getCode(), startTime, endTime);
        result.getRecords().forEach(r ->{
            if (r.getType() == EcologyPointsEnum.HATCH ||
                    r.getType() == EcologyPointsEnum.CAST ||
                    r.getType() == EcologyPointsEnum.GIFT_PROPS){
                UserInfo userInfo = userInfoDao.getById(r.getCreateId());
                if (ObjectUtil.isNotNull(userInfo)){
                    r.setUserName(userInfo.getNickName());
                }
            }
        });
        return result;
    }

    @Override
    public UserPointsDetailsStatisticsVO userPointsDetailsStatistics(String userTel) {
        return userEcologyPointsDao.userPointsDetailsStatistics(userTel);
    }

    @Override
    public Page<AdminQuotaLogVO> quotaLog(Long userId, String daoName,  String collectionName,Date startTime, Date endTime, Integer pageNo, Integer pageSize) {
        return userEcologyPointsQuotaLogDao.quotaLog(new Page<>(pageNo,pageSize),userId,daoName,collectionName,startTime,endTime);
    }

    @Override
    public Page<AdminExpiredRecordsVO> expiredRecords(Long userId, EcologyIncomeTypeEnum incomeType, Date startTime, Date endTime, Integer pageNo, Integer pageSize) {
        return userEcologyPointsQuotaRecordsDao.expiredRecords(userId,incomeType,startTime,endTime,new Page<>(pageNo,pageSize));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void initUsableAmountLimit() {
        ArrayList<String> logs = Lists.newArrayList();
        List<SumEcologyPointsGroupUserDTO> list = userEcologyHatchRecordsDao.getSumEcologyPointsGroupUserId();
        for (SumEcologyPointsGroupUserDTO sumEcologyPointsGroupUserDTO : list) {
            BigDecimal sumIncome = userEcologyHatchIncomeRecordDao.getSumIncomeByUserId(sumEcologyPointsGroupUserDTO.getUserId());
            BigDecimal multiply = sumEcologyPointsGroupUserDTO.getSumEcologyPoints().multiply(Convert.toBigDecimal(3));
            BigDecimal subtract = multiply.subtract(sumIncome);
            subtract = subtract.compareTo(BigDecimal.ZERO) < 0 ? BigDecimal.ZERO : subtract;

            logs.add("用户id:"+sumEcologyPointsGroupUserDTO.getUserId()
                    +"  总收益:"+sumIncome
                    +"  总投入燃料:"+sumEcologyPointsGroupUserDTO.getSumEcologyPoints()
                    +"  有效额度:"+multiply
                    +"  有效额度-总收益:"+subtract);


            Assert.isTrue(userEcologyPointsDao.updateUsableAmountUpperLimitByUserId(sumEcologyPointsGroupUserDTO.getUserId(),subtract),()->new GlobalRunTimeException("更新用户燃料上限失败"));
        }

        log.info("初始化用户燃料上限额度日志:{}",logs);
    }

    @Override
    public AdminEcologyPointsDataVO getEcologyPointsData() {
        AdminEcologyPointsDataVO adminEcologyPointsDataVO = new AdminEcologyPointsDataVO();
        adminEcologyPointsDataVO.setSumUsableEcologyPoints(userEcologyPointsDao.getSumAmount());
        adminEcologyPointsDataVO.setSumFrozenEcologyPoints(userEcologyPointsQuotaRecordsDao.getSumFrozenEcologyPoints());
        return adminEcologyPointsDataVO;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void transferMarketValueAccount(Long userId, Long daoId, BigDecimal transferAmount) {
        DaoEcologyPoints daoEcologyPoints = daoEcologyPointsDao.getByDaoId(daoId);
        Assert.notNull(daoEcologyPoints, ()->new GlobalRunTimeException("DAO燃料账户不存在"));
        BigDecimal beforeBalance = daoEcologyPoints.getAmount();
        Assert.isTrue(NumberUtil.isGreaterOrEqual(daoEcologyPoints.getAmount(),transferAmount), ()->new GlobalRunTimeException("可用金额不足"));

        Assert.isTrue(daoEcologyPointsDao.subAmount(daoId, transferAmount),()->new GlobalRunTimeException("扣减可用金额失败"));
        DaoEcologyPointsDetail daoEcologyPointsDetail = new DaoEcologyPointsDetail();
        daoEcologyPointsDetail.setDaoId(daoId);
        daoEcologyPointsDetail.setAmountType(DaoEcologyPointsDetailAmountTypeEnum.AVAILABLE);
        daoEcologyPointsDetail.setType(DaoEcologyPointsDetailRecordsTypeEnum.TRANSFER_OUT_TO_MARKET_VALUE_ACCOUNT);
        daoEcologyPointsDetail.setAmount(transferAmount);
        daoEcologyPointsDetail.setRealAmount(transferAmount);
        daoEcologyPointsDetail.setBeforeBalance(beforeBalance);
        daoEcologyPointsDetail.setAfterBalance(beforeBalance.subtract(transferAmount));
        daoEcologyPointsDetail.setIsAdd(Boolean.FALSE);

        UserInfo user = userInfoDao.getById(userId);
        daoEcologyPointsDetail.setToUserTel(user.getUserTel());
        daoEcologyPointsDetail.setToUserId(userId);
        daoEcologyPointsDetail.insert();

        //玛特后台记录表
        DaoEcologyPointsTransferRecords daoEcologyPointsTransferRecords = new DaoEcologyPointsTransferRecords();
        daoEcologyPointsTransferRecords.setDaoId(daoId);
        daoEcologyPointsTransferRecords.setToUserId(userId);
        daoEcologyPointsTransferRecords.setAmountType(DaoEcologyPointsDetailAmountTypeEnum.AVAILABLE);
        daoEcologyPointsTransferRecords.setType(DaoEcologyPointsDetailRecordsTypeEnum.TRANSFER_TO_MARKET_VALUE_ACCOUNT);
        daoEcologyPointsTransferRecords.setAmount(transferAmount);
        daoEcologyPointsTransferRecords.insert();


        Assert.isTrue(addEcologyPoints(userId,transferAmount,EcologyPointsEnum.COMPANY_ACCOUNT_IN, StrUtil.EMPTY),()->new GlobalRunTimeException("增加用户燃料失败"));
    }

    @Override
    public Page<AdminuserEcologyUsableAmountUpperLimitDetailPageVO> userEcologyUsableAmountUpperLimitDetailPage(Long userId, Date startTime, Date endTime, Integer pageNo, Integer pageSize) {
        return userEcologyUsableAmountUpperLimitDetailDao.userEcologyUsableAmountUpperLimitDetailPage(new Page<>(pageNo,pageSize),userId,startTime,endTime);
    }

    @Override
    public void addUsableAmountUpperLimitByUserId(Long userId, BigDecimal usableAmountUpperLimit, UserEcologyUsableAmountUpperLimitDetailTypeEnum type,String typeName) {

        UserEcologyPoints userEcologyPoints = userEcologyPointsDao.getOneByUserId(userId);
        Assert.notNull(userEcologyPoints,()->new GlobalRunTimeException("用户燃料不存在"));

        BigDecimal beforeBalance = userEcologyPoints.getUsableAmountUpperLimit();
        UserEcologyUsableAmountUpperLimitDetail userEcologyUsableAmountUpperLimitDetail = new UserEcologyUsableAmountUpperLimitDetail();
        userEcologyUsableAmountUpperLimitDetail.setUserId(userId);
        userEcologyUsableAmountUpperLimitDetail.setType(type);
        userEcologyUsableAmountUpperLimitDetail.setTypeName(StrUtil.format("{}:{}",type.getDescription(),typeName));
        userEcologyUsableAmountUpperLimitDetail.setAmount(usableAmountUpperLimit);
        userEcologyUsableAmountUpperLimitDetail.setBeforeBalance(beforeBalance);
        userEcologyUsableAmountUpperLimitDetail.setAfterBalance(beforeBalance.add(usableAmountUpperLimit));
        userEcologyUsableAmountUpperLimitDetail.setIsAdd(type.getIsAdd());
        userEcologyUsableAmountUpperLimitDetail.insert();
        Assert.isTrue(userEcologyPointsDao.addUsableAmountUpperLimitByUserId(userId, usableAmountUpperLimit),()->new GlobalRunTimeException("增加用户燃料上限失败"));
    }

    /**
     * 燃料兑换nft
     *
     * @param toUserId
     * @param daoId
     * @param applyNum
     * @param collectionId
     * @param needPoints
     */
    @Override
    public void pointApplyExchange(Long toUserId, Long daoId, Integer applyNum, Long collectionId, BigDecimal needPoints) {
        Assert.isTrue(daoEcologyPointsDao.subAmount(daoId, needPoints),()->new GlobalRunTimeException("扣减可用金额失败"));
    }
}
